

package com.zdsx.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zdsx.common.exception.ErrorCode;
import com.zdsx.common.exception.CommonException;
import com.zdsx.common.service.impl.BaseServiceImpl;
import com.zdsx.common.validator.AssertUtils;
import com.zdsx.dao.CaffeineRepository;
import com.zdsx.dao.SmsVerifyDao;
import com.zdsx.dao.UserDao;
import com.zdsx.dao.ZdsxUserRoleDao;
import com.zdsx.dto.BindPhoneDTO;
import com.zdsx.dto.LoginDTO;
import com.zdsx.dto.WxLoginDTO;
import com.zdsx.entity.*;
import com.zdsx.service.SysUserService;
import com.zdsx.service.TokenService;
import com.zdsx.service.UserService;
import com.zdsx.service.WxSystemService;
import com.zdsx.utils.RandomUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

@Service
public class UserServiceImpl extends BaseServiceImpl<UserDao, UserEntity> implements UserService {

    private static List<String> VIP_MOBILE = Arrays.asList("15274412143", "18670034144", "17308488371");

    private static List<String> SUPER_VIP_MOBILE = Arrays.asList("15274412143", "18670034144", "17308488371");

    @Resource
    private SysUserService sysUserService;

    @Resource
    private WxSystemService wxSystemService;

    @Resource
    private CaffeineRepository caffeineRepository;

    @Resource
    private TokenService tokenService;

    @Value("${spring.profiles.active}")
    private String active;

    @Resource
    private SmsVerifyDao smsVerifyDao;

    @Resource
    private ZdsxUserRoleDao zdsxUserRoleDao;

    @Override
    public UserEntity getByMobile(String mobile) {
        return baseDao.getUserByMobile(mobile);
    }

    @Override
    public UserEntity getByOpenId(String openId) {
        return baseDao.getUserByOpenId(openId);
    }

    /**
     * 添加系统用户，返回id
     * @param openId
     * @return
     */
    public int insertSysUserEntity(SysUserEntity openId) {
        return baseDao.insertSysUserEntity(openId);
    }

    @Override
    public UserEntity getUserByUserId(Long userId) {
//        String cacheName = "user:info";
//        UserEntity userEntity = caffeineRepository.getExpire(cacheName, userId.toString(), UserEntity.class);
//        if (userEntity == null) {
//            userEntity = baseDao.getUserByUserId(userId);
//            caffeineRepository.setExpire(cacheName, userId.toString(), userEntity, 60*5);// 5分钟有效期
//        }
//        return userEntity;
        return baseDao.getUserByUserId(userId);
    }

    @Transactional
    @Override
    public Map<String, Object> wxLogin(WxLoginDTO dto){

        // code换openid
        String code = dto.getCode();
        String openId = wxSystemService.getOpenId(code);
        if (StringUtils.isBlank(openId)) {
            throw new CommonException("登陆失败，请联系管理员");
        }

        // 判断openid是否注册
        UserEntity user = this.getByOpenId(openId);

        String nickName = Optional.ofNullable(dto.getWxNickName()).orElse("wx-" + RandomUtil.generateRandomString(10));

        // 不存在就自动注册
        if (user == null) {
            user = new UserEntity();
            user.setOpenId(openId);
            user.setMobile("");
            // TODO 获取用户微信名
            user.setUsername(nickName);
            user.setPassword("xxxx");
            user.setCreateDate(new Date());
            user.setGender(dto.getWxGender());
            baseDao.insert(user);
        }

        Long zdsxUserId = user.getId();

        // 注册后台用户：添加到后台用户表里面去
        SysZdsxUserRefEntity ref = baseDao.getSysUserRef(zdsxUserId);
        if (ref == null || ref.getZdsxUserId() == null) {
            SysUserEntity sysUserEntity = SysUserEntity.builder()
                    .username(nickName)
                    .superAdmin(0)
                    .status(1)
                    .gender(2) // 保密
                    .updateDate(new Date())
                    .userType(2)
                    .build();
            sysUserEntity.setCreateDate(new Date());
            sysUserService.insert(sysUserEntity);
            ref = SysZdsxUserRefEntity.builder().sysUserId(sysUserEntity.getId()).zdsxUserId(zdsxUserId).build();
            baseDao.insertZdsxSysUserRef(ref);
        }


        // 注册后自动生成token 获取登录token
        TokenEntity tokenEntity = tokenService.createToken(user.getId());

        // 将时间戳转换为Date对象
        Date date = tokenEntity.getExpireDate();

        // 创建一个SimpleDateFormat对象，并指定日期时间格式
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        // 使用formatter将Date对象格式化为字符串
        String formattedDateTime = formatter.format(date);

        // 判断是不是admin
        String role = this.getUserRole(user.getMobile());

        Map<String, Object> map = new HashMap<>(2);
        map.put("token", tokenEntity.getToken());
        map.put("expireAt", formattedDateTime);
        map.put("role", role);

        return map;
    }

    @Transactional
    @Override
    public void bindPhone(Long userId, BindPhoneDTO dto) {
        String phoneNum = dto.getMobile();
        UserEntity user = this.getUserByUserId(userId);
        if (user == null) {
            throw new CommonException("用户不存在");
        }

        if ("prod".equals(active)) {
            if (StringUtils.isNotBlank(user.getMobile())) {
                throw new CommonException("已经绑定过手机号码，暂不支持自助修改");
            }
        }


        // 输入的验证码，实际的验证码
        String inputCode = dto.getSmsCode();
        String realSms = this.querySmsCode(phoneNum);

        // 生产环境才校验
        if ("prod".equals(active)) {
            if (StringUtils.isBlank(realSms) || !inputCode.equals(realSms)) {
                throw new CommonException("验证码错误或已失效");
            }
        }


        // 清除缓存
        this.delSmsCode(phoneNum);

        UserEntity update = new UserEntity();
        update.setId(userId);
        update.setMobile(phoneNum);
        this.updateById(update);

        //  TODO 修改后台用户绑定手机号码表
        // 查询关联表
        SysZdsxUserRefEntity ref = baseDao.getSysUserRef(userId);
        if (ref == null) {
            // 新增
            SysUserEntity sysUserEntity = SysUserEntity.builder()
                    .username("wx用户")
                    .mobile(phoneNum)
                    .superAdmin(0)
                    .status(1)
                    .updateDate(new Date())
                    .userType(2)
                    .build();
            sysUserEntity.setCreateDate(new Date());
            sysUserEntity.setUpdateDate(new Date());
            sysUserService.insert(sysUserEntity);
            ref = SysZdsxUserRefEntity.builder().sysUserId(sysUserEntity.getId()).zdsxUserId(userId).build();
            baseDao.insertZdsxSysUserRef(ref);
        } else {
            SysUserEntity sysUserEntity = SysUserEntity.builder().build();
            sysUserEntity.setId(ref.getSysUserId());
            sysUserEntity.setMobile(phoneNum);
            sysUserService.updateById(sysUserEntity);
        }

    }


    /**
     * TODO 这里缓存要改成全局的才行
     *   TODO 用户缓存也要改成全局的
     * @param userId
     * @param mobile
     * @param type
     * @return
     */
    @Override
    public Map<String, String> sendVerifySms(Long userId, String mobile, String type) {
        UserEntity user = getUserByUserId(userId);

        // 非现网环境可以修改手机号码
        if ("prod".equals(active)) {
            if (StringUtils.isNotBlank(user.getMobile())) {
                throw new CommonException("已经绑定过手机号码，暂不支持自助修改");
            }
        }

        String smsKey = String.format("sms:type:%s:%s", type, mobile);

        // 查询是否已经有缓存了，如果
        String sms = this.querySmsCode(mobile);

        if (StringUtils.isNotBlank(sms)) {
            throw new CommonException("验证码已发送，请稍后再试");
        }

        sms = RandomUtil.generateVerificationCode(4);
        // 测试环境验证码写死
        if ("dev".equals(active) || "test".equals(active)) {
            sms = "123456";
        }

        // 现网环境才发送验证码
//        if ("prod".equals(active)) {
            wxSystemService.sendVerifySms(mobile, new String[]{sms, "5"});
//        }

        LocalDateTime now = LocalDateTime.now(); // 获取当前时间
        LocalDateTime future = now.plusMinutes(5); // 计算过期时间
        caffeineRepository.setExpire(smsKey, (Object)sms, 60 * 5); // 5分钟

        // 保存短信验证码到数据库（没有缓存，备用）
        Date createdAt = Date.from(now.atZone(ZoneId.systemDefault()).toInstant());
        Date expiredAt = Date.from(future.atZone(ZoneId.systemDefault()).toInstant());
        SmsVerifyEntity smsVerifyEntity = SmsVerifyEntity.builder().mobile(mobile).content(sms).createdAt(createdAt).expiredAt(expiredAt).build();
        smsVerifyDao.insert(smsVerifyEntity);

        Map<String, String> map = new HashMap<>();
//        map.put("smsCode", sms);
//        map.put("expireAt", future.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // 发送验证码
        return map;
    }

    @Override
    public String getUserRole(Long userId) {

        UserEntity user = getUserByUserId(userId);
        String mobile = user.getMobile();
        if (StringUtils.isBlank(mobile)) {
            // TODO 暂时屏蔽
//            throw new CommonException("未绑定手机号码，请先绑定手机号码");
        }

        return getUserRole(mobile);
    }

    public String getUserRole(String mobile) {

        return "2";
        // TODO 暂时屏蔽
//        if (StringUtils.isNotBlank(mobile)) {
//            QueryWrapper<ZdsxUserRole> queryWrapper = new QueryWrapper<>();
//            queryWrapper.eq("mobile", mobile);
//            ZdsxUserRole role = zdsxUserRoleDao.selectOne(queryWrapper);
//            if (role != null) {
//                return role.getRole();
//            }
//        }
//        return "0";
    }

    @Override
    public Map<String, Object> login(LoginDTO dto) {
        UserEntity user = getByMobile(dto.getMobile());
        AssertUtils.isNull(user, ErrorCode.ACCOUNT_PASSWORD_ERROR);

        //密码错误
        if (!user.getPassword().equals(DigestUtil.sha256Hex(dto.getPassword()))) {
            throw new CommonException(ErrorCode.ACCOUNT_PASSWORD_ERROR);
        }

        //获取登录token
        TokenEntity tokenEntity = tokenService.createToken(user.getId());

        Map<String, Object> map = new HashMap<>(2);
        map.put("token", tokenEntity.getToken());
        map.put("expire", tokenEntity.getExpireDate().getTime() - System.currentTimeMillis());

        return map;
    }

    // 查询验证码
    public String querySmsCode(String phoneNum) {
        String smsKey = String.format("sms:type:%s:%s", "bind", phoneNum);
        String sms = (String) caffeineRepository.getExpire(smsKey);
        if (StringUtils.isBlank(sms)) {
            // 从数据库里面拿
            SmsVerifyEntity r = smsVerifyDao.selectEffective(phoneNum);
            if (r != null) {
                sms = r.getContent();
                Date expiredAt = r.getExpiredAt();
                Date now = new Date();
                long expire = Duration.between(expiredAt.toInstant(), now.toInstant()).getSeconds();
                caffeineRepository.setExpire(smsKey, sms, expire);
            }
        }
        return sms;
    }

    public void delSmsCode(String phoneNum) {
        String smsKey = String.format("sms:type:%s:%s", "bind", phoneNum);
        caffeineRepository.delExpire(smsKey);
        smsVerifyDao.deleteByMobile(phoneNum);
    }

}